﻿using MsTestUtilities.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace MsTestUtilities.SolutionAnalysis
{
    public class SolutionFileReader
    {
        private const string GlobalSectionHeader = @"Global";
        private static readonly Regex _projectFilePattern = new Regex(@"Project\(""(?<entryType>[^""]+)""\) = ""(?<name>[^""]+)"", ""(?<path>[^""]+)"", ""(?<id>[^""]+)""");

        private static readonly Dictionary<Guid, EntryType> EntryTypes = new Dictionary<Guid, EntryType>{
            { new Guid("{2150E333-8FDC-42A3-9474-1A3956D46DE8}"), EntryType.Folder},
            { new Guid("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"), EntryType.Project}
        };

        public SolutionInfo ReadFromFile(string solutionFile)
        {
            var lines = File.ReadLines(solutionFile).ToList();
            var solutionInfo = Parse(lines);
            solutionInfo.Path = solutionFile;
            return solutionInfo;
        }

        public SolutionInfo Parse(string solutionFileText)
        {
            Require.ArgumentNotNull(solutionFileText);         

            return Parse(solutionFileText.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
        }

        private SolutionInfo Parse(IList<string> lines)
        {
            Require.ArgumentNotNull(lines);

            var solutionFile = new SolutionInfo();

            solutionFile.FileFormat = lines[0].Split(' ').Last();

            if (lines.Count == 1)
            {
                return solutionFile;
            }

            solutionFile.VisualStudioVersion = ParseVersion(lines, "VisualStudioVersion");
            solutionFile.MinimumVisualStudioVersion = ParseVersion(lines, "MinimumVisualStudioVersion");

            if (lines[1][0] == '#')
            {
                solutionFile.Author = lines[1].Substring(2);
            }

            solutionFile.AddProjects(ParseProjects(lines));

            return solutionFile;
        }

        private Version ParseVersion(IList<string> lines, string tag)
        {
            return lines
                .Where(l => l.StartsWith(tag))
                .Select(l => l.Split(' ').Last())
                .Select(v => new Version(v))
                .FirstOrDefault();
        }

        private IEnumerable<SolutionEntry> ParseProjects(IList<string> lines)
        {
            foreach (var line in lines)
            {
                if (line == GlobalSectionHeader)
                {
                    yield break;
                }

                var match = _projectFilePattern.Match(line);
                if (match.Success)
                {
                    var project = new SolutionEntry
                    {
                        Type = EntryTypes[new Guid(match.Groups["entryType"].Value)],
                        Name = match.Groups["name"].Value,
                        RelativePath = match.Groups["path"].Value,
                        Id = Guid.Parse(match.Groups["id"].Value),
                    };

                    yield return project;
                }
            }
        }
    }
}
